<script setup>
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { Chart } from '@antv/g2' // 引入 G2

// 组件可接收的数据 data autoFit width height
const props = defineProps({
  type: { type: String, default: 'bar' }, // 图表类型 'bar' 柱状图 'pie' 饼图 'area' 面积图
  data: { type: Array, default: () => [] },
  autoFit: { type: Boolean, default: true },
  width: { type: Number, default: 430 },
  height: { type: Number, default: 279 },
})

let chart
const container = ref(null)

// 渲染柱状图表 参数 container是元素节点
function renderBarChart(container) {
  const data = [
    // 示例数据
    { genre: 'Sports', sold: 275 },
    { genre: 'Strategy', sold: 115 },
    { genre: 'Action', sold: 120 },
    { genre: 'Shooter', sold: 350 },
    { genre: 'Other', sold: 150 },
  ]

  // 柱状图表配置项
  const barOptions = {
    axis: {
      x: {
        title: null, // x轴标题不显示
        labelFill: '#fff', // x轴标签颜色
        tickStroke: '#fff', // x轴刻度颜色
        gridStroke: '#444', // x轴网格线颜色
      },
      y: {
        title: null, // y轴标题不显示
        labelFill: '#fff', // y轴标签颜色
        tickStroke: '#fff', // y轴刻度颜色

        grid: true, // 显示y轴网格线
        gridStroke: '#444', // y轴网格线颜色
        gridLineWidth: 2, // 网格线宽度
        gridStrokeOpacity: 0.7, // 网格线透明度
      },
    },
    legend: false, // 不显示图例
    scale: {
      x: {
        type: 'band', // x轴类型 类别型
        paddingInner: 0.5, // 柱子内边距  间接控制带宽
        paddingOuter: 0.5, // 柱子外边距
      },
    },
  }

  // 创建图表实例
  const chart = new Chart({
    container: container,
    autoFit: props.autoFit,
    width: props.width,
    height: props.height,
    marginLeft: 15,
    marginTop: 28,
    padding: [33.5, 50, 50, 33.5], // 图表内边距 参数[top, right, bottom, left]
    ...barOptions, // 合并柱状图配置项
  })

  // 声明可视化
  chart
    .interval() // 创建一个 Interval 标记
    .data(Array.isArray(props.data) && props.data.length ? props.data : data) // 绑定数据
    .encode('x', 'genre') // 编码 x 通道
    .encode('y', 'sold') // 编码 y 通道
    .style('fill', 'linear-gradient(-90deg, rgb(0, 188, 212), rgb(33, 150, 243))') // 渐变色填充柱子

  // 渲染图表
  chart.render()

  // 返回图表实例，以便后续销毁
  return chart
}

// 渲染饼图表 参数 container是元素节点
function renderPieChart(container) {
  const data = [
    // 示例数据
    { genre: 'Sports', sold: 275 },
    { genre: 'Strategy', sold: 115 },
    { genre: 'Action', sold: 120 },
    { genre: 'Shooter', sold: 350 },
    { genre: 'Other', sold: 150 },
  ]

  // 创建图表实例
  const chart = new Chart({
    container: container,
    autoFit: props.autoFit,
    width: props.width,
    height: props.height,
    marginLeft: 15,
    marginTop: 28,
    padding: [33.5, 50, 50, 33.5], // 图表内边距 参数[top, right, bottom, left]
  })

  chart.coordinate({ type: 'theta' }) // 极坐标系

  // 声明可视化
  chart
    .interval() // 创建一个 Interval 标记
    .data(Array.isArray(props.data) && props.data.length ? props.data : data) // 绑定数据
    .transform({ type: 'stackY' }) // 堆叠
    .encode('y', 'sold') // 编码 y 通道
    .encode('key', 'genre') // 指定 key
    .animate('update', { duration: 300 }) // 指定更新动画的时间
    .label({
      // 显示标签
      text: (d) => `${d.genre}\n${d.sold}`,
      radius: 0.8,
      fontSize: 12,
      fontWeight: 400,
    })
    .style({
      // 设置饼图边框
      stroke: '#fff',
    })
    .encode('color', 'genre') // 绑定颜色通道
    .scale('color', {
      // type: 'ordinal' 离散型数据 range: 颜色数组
      type: 'ordinal',
      range: [
        'rgb(103, 58, 183)',
        'rgb(63, 81, 181)',
        'rgb(33, 150, 243)',
        'rgb(3, 169, 244)',
        'rgb(0, 188, 212)',
      ],
    })

  // 配置图例 参数：
  chart.legend('color', {
    orientation: 'vertical', // 垂直排列
    position: 'right', // 右侧
    layout: {
      // 布局
      justifyContent: 'center', // 主轴column居中
    },
    itemLabelFill: '#fff', // 图例标签颜色
  })
  // 渲染图表
  chart.render()
  return chart
}

// 渲染面积图
function renderAreaChart(container) {
  // 示例数据
  const data = [
    { genre: 'Sports', sold: 275 },
    { genre: 'Strategy', sold: 115 },
    { genre: 'Action', sold: 120 },
    { genre: 'Shooter', sold: 350 },
    { genre: 'Other', sold: 150 },
  ]

  // 面积图表配置项
  const areaOptions = {
    axis: {
      x: {
        title: null, // x轴标题不显示
        labelFill: '#fff', // x轴标签颜色
        tickStroke: '#fff', // x轴刻度颜色
        gridStroke: '#444', // x轴网格线颜色
      },
      y: {
        title: null, // y轴标题不显示
        labelFill: '#fff', // y轴标签颜色
        tickStroke: '#fff', // y轴刻度颜色

        grid: true, // 显示y轴网格线
        gridStroke: '#444', // y轴网格线颜色
        gridLineWidth: 2, // 网格线宽度
        gridStrokeOpacity: 0.7, // 网格线透明度
      },
    },
    legend: false, // 不显示图例
  }

  // 创建图表实例
  const chart = new Chart({
    container: container,
    autoFit: props.autoFit,
    width: props.width,
    height: props.height,
    marginLeft: 15,
    marginTop: 28,
    padding: [33.5, 50, 50, 33.5], // 图表内边距 参数[top, right, bottom, left]
    insetLeft: 10,
    insetRight: 10,
    ...areaOptions, // 合并面积图配置项
  })

  // 声明可视化
  chart
    .area() // 创建一个 Area 标记
    .data(Array.isArray(props.data) && props.data.length ? props.data : data) // 绑定数据
    .encode('x', 'genre') // 编码 x 通道
    .encode('y', 'sold') // 编码 y 通道
    .encode('shape', 'smooth') // 曲线形状
    .style('fill', 'linear-gradient(to bottom,rgba(0, 188, 212, 1) ,rgba(0, 188, 212, 0) )')

  chart
    .line() // 创建一个 Line 标记
    .data(Array.isArray(props.data) && props.data.length ? props.data : data) // 绑定数据
    .encode('x', 'genre') // 编码 x 通道
    .encode('y', 'sold') // 编码 y 通道
    .encode('shape', 'smooth') // 曲线形状
    .style('stroke', 'rgb(0, 188, 212)') // 绑定颜色通道
    .style('lineWidth', 2) // 线宽
  // 渲染图表
  chart.render()

  // 返回图表实例，以便后续销毁
  return chart
}

onMounted(() => {
  // 根据传入的type决定渲染哪种图表
  if (props.type === 'bar') {
    chart = renderBarChart(container.value)
  } else if (props.type === 'pie') {
    chart = renderPieChart(container.value)
  } else if (props.type === 'area') {
    chart = renderAreaChart(container.value)
  } else {
    console.warn(`Unsupported chart type: ${props.type}`)
  }
})
// 组件卸载销毁图表
onBeforeUnmount(() => {
  if (chart) {
    chart.destroy()
    chart = null
  }
})
</script>

<template>
  <div
    id="container"
    ref="container"
    :style="{ width: props.width + 'px', height: props.height + 'px' }"
  ></div>
</template>

<style scoped>
#container {
  border: 1px solid #ccc;
  border-radius: 5px;
  box-sizing: border-box;
}
</style>
